﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazz is open software: you can redistribute it and/or modify it 
// under the terms of the GNU Affero General Public License (AGPL) as published by 
// the Free Software Foundation, version 3 of the License.
// 
// Jazz is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty 
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
//  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public 
// License along with Jazz.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TrackerRealm.Jazz.Client;
using PostSharp.Extensibility;
using TrackerRealm.Jazz.Client.Custom;

namespace JazzClientTest
{
    /// <summary>
    /// A test suite that demonstrates using of the States within Jazz.
    /// For more information on states see - http://wiki.jazzfire.net/State.ashx
    /// </summary>
    [TestClass]
    public class StateTests
    {
        #region StateExample_HappyPath_Verbose
        /// <summary>
        /// This demonstrates a correct path of execution.
        /// That is data is entered, then it is approved.
        /// See the Console output to see the details of the operations
        /// </summary>
        [TestMethod]
        public void StateExample_HappyPath_Verbose()
        {
            using (ClientNexus nexus = ClientNexus.Create(NexusConfig.DefaultVerbose()))
            {
                SimpleRequest myFlow = new SimpleRequest();
                nexus.Bind(myFlow);
                //
                // Enter some data - this will change the state the Approval state.
                myFlow.Request = "May I leave early on Friday?";
                //
                // Approve the workflow
                myFlow.Approve();
            }
        }
        #endregion
        #region StateExample_HappyPath_CheckStates
        /// <summary>
        /// This demonstrates a correct path of execution.
        /// That is data is entered, then it is approved.
        /// </summary>
        [TestMethod]
        public void StateExample_HappyPath_CheckStates()
        {
            using (ClientNexus nexus = ClientNexus.Create())
            {
                SimpleRequest myFlow = new SimpleRequest();
                nexus.Bind(myFlow);
                //
                // begin in the 'Start' State
                Assert.AreEqual("Start", myFlow.CurrentState.Name);
                //
                // Enter some data - this will change the state the Approval state.
                myFlow.Request = "May I leave early on Friday?";
                Assert.AreEqual("Approval", myFlow.CurrentState.Name);
                //
                // Approve the workflow
                myFlow.Approve();
                //
                // Now the workflow is in the Completed State.
                Assert.AreEqual("Completed", myFlow.CurrentState.Name);
            }
        }
        #endregion

        #region StateExample_ErrorConditions
        /// <summary>
        /// This demonstrates that an exception occurs when access is not allowed.
        /// </summary>
        [TestMethod]
        public void StateExample_ErrorConditions()
        {
            using (ClientNexus nexus = ClientNexus.Create(NexusConfig.DefaultVerbose()))
            {
                SimpleRequest myFlow = new SimpleRequest();
                nexus.Bind(myFlow);
                
                //
                // Enter some data - this will change the state the Approval state.
                myFlow.Request = "May I leave early on Friday?";

                try
                {
                    //
                    // Enter some more data.  This will cause an exception.
                    myFlow.Request = "I changed my mind, I want to leave Friday at noon.";
                }
                catch (Exception ex)
                {
                    Assert.IsTrue(ex is AccessViolationException);
                }

                // 'Data' has not been changed.
                Assert.AreEqual("May I leave early on Friday?", myFlow.Request);
            }
        }
        #endregion

        #region StateExample_WhichMembersAreAccessible
        /// <summary>
        /// This demonstrates which members are accessible in which state.
        /// This is useful for an application that would like to display operations and attributes of a workflow.
        /// </summary>
        [TestMethod]
        public void StateExample_WhichMembersAreAccessible()
        {
            using (ClientNexus nexus = ClientNexus.Create())
            {
                SimpleRequest myFlow = new SimpleRequest();
                nexus.Bind(myFlow);
                //
                // begin in the 'Start' State
                Assert.AreEqual("Start", myFlow.CurrentState.Name);
                //
                //  Start State
                //        --> Request
                //             --> get_Request
                //             --> set_Request
                //        --> IsApproved
                //             --> get_IsApproved
                //
                jMemberInfo[] wrappedMembers = myFlow.GetWrappedMembers().UI().ToArray();
                Assert.AreEqual(3, wrappedMembers.Length);

                jMemberInfo approve = wrappedMembers.First(m => m.Name == "Approve");
                jMemberInfo request = wrappedMembers.First(m => m.Name == "Request");
                jMemberInfo isApproved = wrappedMembers.First(m => m.Name == "IsApproved");
                
                //
                // check Request
                Assert.AreEqual("Request", request.Name);
                Assert.IsTrue(request.IsProperty);
                Assert.IsTrue(request.CanRead);
                Assert.IsTrue(request.CanWrite);
                Assert.IsFalse(request.ReadAccessor.IsGrey);
                Assert.IsFalse(request.WriteAccessor.IsGrey);
                //
                // check isApproved
                Assert.AreEqual("IsApproved", isApproved.Name);
                Assert.IsTrue(isApproved.IsProperty);
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsFalse(isApproved.CanWrite);
                // check Approve
                Assert.IsFalse(approve.IsPublic);
                Assert.IsTrue(approve.IsGrey);


                //
                // Enter some data - this will change the state the Approval state.
                request.Write(myFlow, "May I leave early on Friday?");
                Assert.AreEqual("Approval", myFlow.CurrentState.Name);
                Assert.AreEqual("May I leave early on Friday?", request.Read(myFlow));
                //
                //  Approval State
                //        --> Request
                //           --> get_Request
                //        --> IsApproved
                //           --> get_IsApproved
                //        --> Approve
                //
                
                //
                // check Request
                Assert.AreEqual("Request", request.Name);
                Assert.IsTrue(request.IsProperty);
                Assert.IsTrue(request.CanRead);
                Assert.IsFalse(request.CanWrite);
                //
                // check isApproved
                Assert.AreEqual("IsApproved", isApproved.Name);
                Assert.IsTrue(isApproved.IsProperty);
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsFalse(isApproved.CanWrite);
                Assert.IsFalse((bool)isApproved.Read(myFlow));
                //
                // check isApproved
                Assert.AreEqual("Approve", approve.Name);
                Assert.IsTrue(approve.IsMethod);
                Assert.IsTrue(approve.IsPublic);
                //
                // Approve the workflow
                approve.Execute(myFlow);
                //
                // Now the workflow is in the Completed State.
                Assert.AreEqual("Completed", myFlow.CurrentState.Name);
                //
                //  Completed State
                //        --> Request
                //              --> get_Request
                //        --> IsApproved
                //              --> get_IsApproved
                //
                
                //
                // check Request
                Assert.AreEqual("Request", request.Name);
                Assert.IsTrue(request.IsProperty);
                Assert.IsTrue(request.CanRead);
                Assert.IsFalse(request.CanWrite);
                //
                // check isApproved
                Assert.AreEqual("IsApproved", isApproved.Name);
                Assert.IsTrue(isApproved.IsProperty);
                Assert.IsTrue(isApproved.CanRead);
                Assert.IsFalse(isApproved.CanWrite);
                // check Approve
                Assert.IsFalse(approve.IsPublic);
            }
        }
        #endregion
        #region SimpleRequest Class - Used in all tests in this class
        /// <summary>
        /// This is a small class that demonstrates the use of states in
        /// a personel request.
        /// The class is very simple only allowing a request to made and
        /// then approved.
        /// The example has been kept simple to better demonstrate the 'state' concepts.
        /// ***********
        /// The '2.RoleTests' builds on this example.
        /// </summary>
        [Jazz]
        public class SimpleRequest : jObject
        {
            #region Constructor
            /// <summary>
            /// Constructor
            /// </summary>
            public SimpleRequest()
            {
                this.data = "";
                this.IsApproved = false;
            }
            #endregion
            #region Request
            private string data;
            /// <summary>
            /// Make a request by setting the data.
            /// The Request property can only be set when
            /// the current state is the 'Start' state.
            /// </summary>
            [UI]
            public string Request
            {
                get { return this.data; }
                [GrantStates("Start")]
                set
                {
                    this.data = value;
                    // prevent the data from being changed.
                    this.EnterState("Approval");
                }
            }
            #endregion
            #region IsApproved
            /// <summary>
            /// Returns true when the Request has been approved.
            /// </summary>
            [UI]
            public bool IsApproved
            {
                get;
                private set;
            }
            #endregion
            #region Approve
            /// <summary>
            /// The approve method is only allowed to be executed when this instance is the 'Approval State'.
            /// </summary>
            [UI]
            [GrantStates("Approval")]
            public void Approve() 
            {
                this.IsApproved = true;
                // prevent the workflow from being approved again.
                this.EnterState("Completed");
            }
            #endregion
            #region States
            //
            // A PersonnelRequest instance has its 'CurrentState' set to one
            // of the following states.
            //
            #region Start
            [StartState]
            void Start()
            {
                // this will be written to the console when the state is entered.
                Console.WriteLine(this.ToString() + ".StartMsg:\t\t***** Getting Started ******");
            }
            #endregion
            #region Completed
            [State]
            void Completed()
            {
                // this will be written to the console when the state is entered.
                Console.WriteLine(this.ToString() + ".CompletedMsg:\t\t***** Completed ******");
            }
            #endregion
            #region Approval
            [FinalState]
            void Approval() 
            {
            }
            #endregion
            #endregion
        }
        #endregion
    }
}
